﻿namespace Deppton.Mobile
{
    using System.Linq;
    using Deppton.Model;
    using Deppton.Mobile.Views;
    using System.Collections.Specialized;
    using System.Collections.Generic;

    public class SurveyController
    {
        public SurveyController(IServicesPortfolio servicesPortfolio)
        {
            this.ServicesPortfolio = servicesPortfolio;
        }

        public IServicesPortfolio ServicesPortfolio
        {
            get;
            set;
        }

        public void RunSurvey(string surveyId)
        {
            this.RunSurvey(surveyId, true);
        }

        public void RunSurvey(string surveyId, bool enableControls)
        {
            var survey = this.ServicesPortfolio.SurveysStorageService.Retrieve(surveyId);
            var surveyDefinition = this.ServicesPortfolio.SurveyDefinitionsStorageService.RetrieveSurveyDefinition(survey.SurveyDefinitionId);

            QuestionForm questionForm = null;
            Question currentQuestion = EvaluateNextQuestion(survey, null, surveyDefinition); ;
            var finish = false;

            do
            {
                switch (currentQuestion.Kind)
                {
                    case QuestionKind.Checks:
                        questionForm = new ChecksQuestionForm();
                        var checksQuestionForm = questionForm as ChecksQuestionForm;

                        SetupChecksCuestionForm(survey, currentQuestion, surveyDefinition, checksQuestionForm, enableControls);

                        if (questionForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                        {
                            survey.QuestionValues[currentQuestion.Id] = new KeyValuePair<string, string>(currentQuestion.Id, questionForm.Value);
                        }
                        break;

                    case QuestionKind.Options:
                        questionForm = new OptionsQuestionForm();
                        var optionsQuestionForm = questionForm as OptionsQuestionForm;

                        SetupOptionsCuestionForm(survey, currentQuestion, surveyDefinition, optionsQuestionForm, enableControls);

                        if (questionForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                        {
                            survey.QuestionValues[currentQuestion.Id] = new KeyValuePair<string, string>(currentQuestion.Id, questionForm.Value);
                        }
                        break;
                    case QuestionKind.GpsPosition:
                        questionForm = new GpsQuestionForm();
                        var gpsQuestionForm = questionForm as GpsQuestionForm;

                        SetupGpsQuestionForm(survey, currentQuestion, surveyDefinition, gpsQuestionForm, enableControls);

                        if (questionForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                        {
                            survey.QuestionValues[currentQuestion.Id] = new KeyValuePair<string, string>(currentQuestion.Id, questionForm.Value);
                        }
                        break;
                }

                switch (questionForm.RequestedAction)
                {
                    case QuestionFormAction.GoPrevious:
                        currentQuestion = EvaluatePreviousQuestion(survey, currentQuestion, surveyDefinition);
                        break;
                    case QuestionFormAction.GoNext:
                        currentQuestion = EvaluateNextQuestion(survey, currentQuestion, surveyDefinition);
                        finish = currentQuestion == null;
                        break;
                    case QuestionFormAction.Finish:
                    case QuestionFormAction.GoBack:
                        finish = true;
                        break;
                }
            } while (!finish && currentQuestion != null);

            if (SurveyIsCompleted(survey, surveyDefinition))
            {
                survey.Status = SurveyStatus.Completed;

                this.ServicesPortfolio.SurveysStorageService.Store(survey);
            }
        }

        private void SetupGpsQuestionForm(Survey survey, Question currentQuestion, SurveyDefinition surveyDefinition, GpsQuestionForm gpsQuestionForm, bool enableControls)
        {
            SetupQuestionForm(survey, currentQuestion, surveyDefinition, gpsQuestionForm, enableControls);

            gpsQuestionForm.QuesitonMetadata = survey.QuestionsMetadata[currentQuestion.Id] != null ? survey.QuestionsMetadata[currentQuestion.Id].Metadata : new NamedList<KeyValuePair<string, string>>(kv => kv.Key);
        }

        private bool SurveyIsCompleted(Survey survey, SurveyDefinition surveyDefinition)
        {
            foreach (var question in surveyDefinition.Questions.Where(q => q.Required))
            {
                if (string.IsNullOrEmpty(survey.QuestionValues[question.Id].Value))
                {
                    return false;
                }
            }

            if (surveyDefinition.CompletedConditionRulesetId != null)
            {
                return surveyDefinition.RulesetDefinitions[surveyDefinition.CompletedConditionRulesetId]
                    .Evaluate(k => EvaluateQuestionValue(survey, k), k => EvaluateQuestionMetadata(survey, k));
            }

            return true;
        }

        private void SetupOptionsCuestionForm(Survey survey, Question question, SurveyDefinition surveyDefinition, OptionsQuestionForm form, bool enableControls)
        {
            SetupQuestionForm(survey, question, surveyDefinition, form, enableControls);

            foreach (var option in question.Options)
            {
                form.Options.Add(option);
            }

            form.Value = survey.QuestionValues[question.Id].Value;
        }

        private void SetupChecksCuestionForm(Survey survey, Question question, SurveyDefinition surveyDefinition, ChecksQuestionForm form, bool enableControls)
        {
            SetupQuestionForm(survey, question, surveyDefinition, form, enableControls);

            foreach (var option in question.Options)
            {
                form.Options.Add(option);
            }

            form.Value = survey.QuestionValues[question.Id].Value;
        }

        private void SetupQuestionForm(Survey survey, Question question, SurveyDefinition surveyDefinition, QuestionForm questionForm, bool enableControls)
        {
            questionForm.AllowGoPrevious = EvaluatePreviousQuestion(survey, question, surveyDefinition) != null;
            questionForm.AllowGoNext = true;
            questionForm.AllowGoPrevious = true;
            questionForm.AllowFinish = true;
            questionForm.Description = question.Description;
            questionForm.ControlsEnabled = enableControls;

            if (question.Condition != null)
            {
                questionForm.ConditionEvaluator = new System.Func<string, bool>
                    (value =>
                surveyDefinition.RulesetDefinitions[question.Condition].Evaluate(
                identifier => identifier == question.Id ? value : EvaluateQuestionValue(survey, identifier),
                identifier => EvaluateQuestionMetadata(survey, identifier)));
            }
            else
            {
                questionForm.ConditionEvaluator = new System.Func<string, bool>(v => true);
            }
        }

        public static Question EvaluateNextQuestion(Survey survey, Question currentQuestion, SurveyDefinition surveyDefinition)
        {
            if (currentQuestion == null)
            {
                return surveyDefinition.Questions.FirstOrDefault();
            }

            var list = surveyDefinition.Questions;

            for (var questionIndex = list.IndexOf(currentQuestion) + 1; questionIndex < surveyDefinition.Questions.Count; questionIndex++)
            {
                var question = list[questionIndex];

                if (question.Precondition != null)
                {
                    if (!surveyDefinition.RulesetDefinitions[question.Precondition]
                        .Evaluate(k => EvaluateQuestionValue(survey, k), k => EvaluateQuestionMetadata(survey, k)))
                    {
                        continue;
                    }
                    else
                    {
                        return question;
                    }
                }
                else
                {
                    return question;
                }
            }

            return null;
        }

        public static string EvaluateQuestionValue(Survey survey, string identifier)
        {
            return survey.QuestionValues.Select(kv => kv.Key).Any(q => string.Equals(q, identifier, System.StringComparison.OrdinalIgnoreCase)) ? survey.QuestionValues[identifier].Value : null;
        }

        public static Question EvaluatePreviousQuestion(Survey survey, Question currentQuestion, SurveyDefinition surveyDefinition)
        {
            if (currentQuestion == null)
            {
                return null;
            }

            var list = surveyDefinition.Questions;

            for (var questionIndex = list.IndexOf(currentQuestion) - 1; questionIndex >= 0; questionIndex--)
            {
                var question = list[questionIndex];

                if (question.Precondition != null)
                {
                    if (!surveyDefinition.RulesetDefinitions[question.Precondition]
                        .Evaluate(k => survey.QuestionValues[k].Value, k => EvaluateQuestionMetadata(survey, k)))
                    {
                        continue;
                    }
                    else
                    {
                        return question;
                    }
                }
                else
                {
                    return question;
                }
            }

            return null;
        }

        public static NamedList<KeyValuePair<string, string>> EvaluateQuestionMetadata(Survey survey, string identifier)
        {
            return survey.QuestionsMetadata[identifier] != null ? survey.QuestionsMetadata[identifier].Metadata : new NamedList<KeyValuePair<string, string>>(kv => kv.Key);
        }

        public void ReviewSurvey(string surveyId)
        {
            this.RunSurvey(surveyId, false);
        }
    }
}
